
// Copyright (c) WanSheng Intelligent Corp. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.


#include "modbus_coding.h"

#include <string.h>
#include <arpa/inet.h>
#include "misc.h"

#define PRINT_BYTES(...)
#define UNUSED(x) (void)(x)

const char * SZ_CODING[Coding_Total_Size] = {"", "abcd","cdab", "dcba", "badc","abcdi","cdabi", "dcbai", "badci" };

mb_coding_e parse_modbus_coding(const char * code)
{
    for(int i=1;i<Coding_Total_Size;i++)
    {
        if(strcasecmp(SZ_CODING[i], code) == 0)
        {
            return i;
        }
    }
    return Coding_Invalid;
}

const char * modbus_coding_text(mb_coding_e coding)
{
    if(coding < 0)
        return "Invalid";

    if(coding < Coding_Total_Size)
        return SZ_CODING[coding];

    return "OverCoding";
}

const char * SZ_MB_REG_TYPES[MB_Max_Types] = {"Discrete", "Coil", "Input", "Holding"};
mb_reg_type_e check_mb_reg_type(const char* type_name)
{
    if(type_name == NULL)
        return MB_Invalid_Reg_Type;

    for(int i=0;i<MB_Max_Types;i++)
    {
        if(strcasecmp(SZ_MB_REG_TYPES[i], type_name) == 0)
            return (mb_reg_type_e) i;
    }

    return MB_Invalid_Reg_Type;
}

const char * mb_reg_type_text(mb_reg_type_e t)
{
    if(t<0) return "Invalid";
    if(t<MB_Max_Types) return SZ_MB_REG_TYPES[t];
    return "Invalid (over)";
}



void for_libmodbus_send(void * p, uint16_t *dest)
{
    // the libmodbus will do hton again befor sending
    uint16_t * elems = (uint16_t * ) p;
    dest[0] = ntohs(elems[0]);
    dest[1] = ntohs(elems[1]);

}

// ensure the value is sent in abcd over the network.
void my_modbus_set_abcd(uint32_t * p, uint16_t *dest)
{
    uint32_t i = htonl(*p);
    for_libmodbus_send(&i, dest);
}

/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
void my_modbus_set_dcba(uint32_t * p, uint16_t *dest)
{
    uint32_t i = htonl(*p);

    uint8_t d[4];
    uint8_t *s = (uint8_t*)&i;
    d[0] = s[3];
    d[1] = s[2];
    d[2] = s[1];
    d[3] = s[0];
    for_libmodbus_send(d, dest);
}

/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
void my_modbus_set_badc(uint32_t * p, uint16_t *dest)
{
    uint32_t i = htonl(*p);

    uint8_t d[4];
    uint8_t *s = (uint8_t*) &i;
    d[0] = s[1];
    d[1] = s[0];
    d[2] = s[3];
    d[3] = s[2];
    for_libmodbus_send(d, dest);    
}

/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
void my_modbus_set_cdab(uint32_t * p, uint16_t *dest)
{
    uint32_t i = htonl(*p);

    uint16_t d[2];
    uint16_t *s = (uint16_t*) &i;
    d[0] = s[1];
    d[1] = s[0];
    for_libmodbus_send(d, dest);    
}

float my_modbus_get_float_abcd(const uint16_t *src, uint32_t * dest)
{
    float f;
    uint16_t elements[2];
    uint32_t* pInt32 = (uint32_t*) elements;

    unsigned char * p = (unsigned char *) src;
    PRINT_BYTES("modbus_get_float_abcd:src, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);


    elements[0] =  htons(src[0]);
    elements[1] =  htons(src[1]);

    p = (unsigned char *) elements;
    PRINT_BYTES("modbus_get_float_abcd:elements, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);

    *dest = ntohl(*pInt32);
    memcpy(&f, dest, sizeof(float));


    p = (unsigned char *) &f;
    PRINT_BYTES("modbus_get_float_abcd:f2=%f, i--> %02X %02X %02X %02X \n",f,p[0],p[1],p[2],p[3]);
    UNUSE(p);
    return f;
}


/* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */
float my_modbus_get_float_cdab(const uint16_t *src, uint32_t * dest)
{
    float f;
    uint16_t elements[2];
    uint32_t* pInt32 = (uint32_t*) elements;

    unsigned char * p = (unsigned char *) src;
    PRINT_BYTES("my_modbus_get_float_cdab:src, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);


    elements[0] =  htons(src[1]);
    elements[1] =  htons(src[0]);
    *dest = ntohl(*pInt32);
    memcpy(&f, dest, sizeof(float));

    p = (unsigned char *) &f;
    PRINT_BYTES("my_modbus_get_float_cdab: f=%f,%02X %02X %02X %02X \n",f, p[0],p[1],p[2],p[3]);

    UNUSE(p);
    return f;
}


/* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */
float my_modbus_get_float_dcba(const uint16_t *src, uint32_t * dest)
{
    float f;
    uint16_t elements[2];

    unsigned char * p = (unsigned char *) src;
    PRINT_BYTES("my_modbus_get_float_dcba:src, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);


    elements[0] =  htons(src[0]);
    elements[1] =  htons(src[1]);
    p = (unsigned char *) elements;
    PRINT_BYTES("modbus_get_float_abcd:elements, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);

    uint8_t *d = (uint8_t*) dest;
    uint8_t *s = (uint8_t*) elements;
    d[0] = s[3];
    d[1] = s[2];
    d[2] = s[1];
    d[3] = s[0];
    p = (unsigned char *) d;
    PRINT_BYTES("modbus_get_float_abcd:dest, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);

    *dest = ntohl(*((uint32_t*)d));
    memcpy(&f, dest, sizeof(float));


    p = (unsigned char *) &f;
    PRINT_BYTES("my_modbus_get_float_dcba: f=%f,%02X %02X %02X %02X \n",f, p[0],p[1],p[2],p[3]);
    UNUSE(p);
    return f;
}


/* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */
float my_modbus_get_float_badc(const uint16_t *src, uint32_t * dest)
{
    float f;
    uint32_t i;
    uint16_t elements[2];

    unsigned char * p = (unsigned char *) src;
    PRINT_BYTES("my_modbus_get_float_badc:src, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);


    elements[0] =  htons(src[0]);
    elements[1] =  htons(src[1]);
    p = (unsigned char *) elements;
    PRINT_BYTES("my_modbus_get_float_badc:elements, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);
    uint8_t *d = (uint8_t*) &i;
    uint8_t *s = (uint8_t*) elements;
    d[0] = s[1];
    d[1] = s[0];
    d[2] = s[3];
    d[3] = s[2];
    p = (unsigned char *) d;
    PRINT_BYTES("my_modbus_get_float_badc:dest, %02X %02X %02X %02X \n",p[0],p[1],p[2],p[3]);

    *dest = ntohl(*((uint32_t*)d));
    memcpy(&f, dest, sizeof(float));
    UNUSE(p);
    return f;
}
